﻿<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
<title>Пересечение отрезка с прямоугольником</title>
<link rel="stylesheet" type="text/css" href="../lib/styles.css"/>
<script type="text/javascript" src="../lib/jquery-1.7.2.min.js"></script>
<script type="text/javascript" src="../lib/raphael-min.js"></script>
<script type="text/javascript">
	$(function(){
		var paper = new Raphael($("#paper1")[0]);
		var ptSize = 5;
		var segment = [[100, 100], [250, 250]];
		
		Raphael.fn.segment = function(segmDef, color){
			var p1 = segmDef[0], p2 = segmDef[1];
			var sgmt = paper.path(["M", p1.join(), "L", p2.join()]).attr({stroke:color}),
				pt1 = paper.circle(p1[0], p1[1], ptSize, ptSize).attr({fill:"#fff", stroke:color, "stroke-width":1, cursor:"move"})
					.drag(dragmove, dragstart, dragend)
					.data({ptIdx:0, segment: sgmt}),
				pt2 = paper.circle(p2[0], p2[1], ptSize, ptSize).attr({fill:color, "stroke-width":0, cursor:"move"})
					.drag(dragmove, dragstart, dragend)
					.data({ptIdx:1, segment: sgmt});
				
				function dragstart(x, y, e) {
					this.data("current_transform", this.transform());
				}
				function dragmove(dx, dy, x, y, e) {
					this.transform(this.data("current_transform")+'T'+dx+','+dy);
					var sgmt = this.data("segment"),
						ptIdx = this.data("ptIdx"),
						segIdx = this.data("segIdx");
					var path = sgmt.attr("path");
					var bbox = this.getBBox();
					
					path[ptIdx][1] = segment[ptIdx][0] = (bbox.x+bbox.x2)/2;
					path[ptIdx][2] = segment[ptIdx][1] = (bbox.y+bbox.y2)/2;
					sgmt.attr("path", path);
					
					var intersection = getIntersection();
					if(intersection)
						intersectionPt.show().attr({cx:intersection.x, cy:intersection.y});
					else
						intersectionPt.hide();
					
				}
				function dragend(e) {
					this.data("current_transform", this.transform());
				}
			
			return paper.set(sgmt, pt1, pt2);
		};
		
		var rectIntersection = (function(){
			
			function segmIntersection(segm1, segm2){
				if(segm1[0][0]==segm1[1][0]) return verticalIntersection(segm2, segm1[0][0], segm1[0][1], segm1[1][1]);
				if(segm2[0][0]==segm2[1][0]) return verticalIntersection(segm1, segm2[0][0], segm2[0][1], segm2[1][1]);
				
				var f1 = getSegmFunc(segm1), f2 = getSegmFunc(segm2);
				var dA = f1.a - f2.a;
				if(!dA) dA = 1e-9; //exclude division by Zero
				var x = (f2.b - f1.b)/dA,
					y = f1.a*x + f1.b;
				
				if(
					between(x, segm1[0][0], segm1[1][0]) &&
					between(x, segm2[0][0], segm2[1][0]) &&
					between(y, segm1[0][1], segm1[1][1]) &&
					between(y, segm2[0][1], segm2[1][1])
				)
					return {x: x, y: y};
				else
					return null;
			}
			
			function verticalIntersection(segm, x, y1, y2){
				if(!between(x, segm[0][0], segm[1][0])) return;
				var f = getSegmFunc(segm);
				var y = f.a*x + f.b;
				if(between(y, y1, y2)) return {x:x, y:y};
			}
			
			function between(x, a, b){
				var arr = [a, x, b];
				arr = arr.sort(function(x1,x2){return x1==x2?0:x1<x2?-1:1;});
				return arr[1]===x;
			}
			
			function getSegmFunc(sgmt){
				var p1 = sgmt[0], p2 = sgmt[1];
				var dX = p2[0] - p1[0];
				if(!dX) dX = 1e-9; // exclude division by Zero
				var a = (p2[1] - p1[1])/dX;
				return {a: a, b: p1[1] - a*p1[0]};
			}

			
			
			function rInter(segm, rect){
				var sides = [
					[[rect.x, rect.y], [rect.x+rect.width, rect.y]],
					[[rect.x, rect.y], [rect.x, rect.y+rect.height]],
					[[rect.x+rect.width, rect.y], [rect.x+rect.width, rect.y+rect.height]],
					[[rect.x+rect.width, rect.y+rect.height], [rect.x, rect.y+rect.height]]
				];
				var interPoints = [];
				for(var side,i=0; side=sides[i],i<sides.length; i++){
					var iPt = segmIntersection(segm, side);
					if(iPt) interPoints.push(iPt);
				}
				if(!interPoints.length) return null;
				
				var x0 = segm[0][0], y0 = segm[0][1];
				interPoints = interPoints.sort(function(a, b){
					var la2 = (a.x-x0)*(a.x-x0)+(a.y-y0)*(a.y-y0);
					var lb2 = (b.x-x0)*(b.x-x0)+(b.y-y0)*(b.y-y0);
					return la2<lb2?-1:la2>lb2?1:0;
				});
				return interPoints[0];
			}
			
			return rInter;
		})();

		paper.segment(segment, "#008");
		
		var path = paper.path("M215,215L250,180L280,120L240,160L200,120Z").attr({fill:"#0f0", stroke:"#040"});
		var bbox = path.getBBox();
		var bboxIcon = paper.rect(bbox.x, bbox.y, bbox.width, bbox.height).attr({stroke:"#ddd"});
		
		var intersection = getIntersection();
		var intersectionPt = paper.circle(intersection?intersection.x:-100, intersection?intersection.y:-100, ptSize, ptSize).attr({fill:"#f00", "stroke-width":0});
		if(!intersection) intersectionPt.hide();
		
		function getIntersection(){
			return rectIntersection(segment, bbox);
		}
		

	});
</script>
</head>
<body>
	<h1>Пересечение отрезка с прямоугольником</h1>
	<p>Указания: отрезок можно перемещать за концы, при этом отображается точка пересечения (ближайшая к началу отрезка, отмеченному пустой точкой) с прямоугольником, описанным вокруг фигуры.</p>
	<!--p>Источник: [1], стр. 36 "Better dragging"</p>
	<p>Особенности:</p>
	<ul>
		<li>используются обработчики события <span class="code">drag</span></li>
		<li>для перемещения объекта используется преобразование <span class="code">transform</span>, а не прямые присваивания координат - это позволяет однообразно перемещать любые типы фигур</li>
		<li>для сохранения текущего состояния преобразования объекта используется стандартный метод <span class="code">data()</span></li>
	</ul-->
	<div id="paper1" class="paper" style="width:600px; height:400px;">
	</div>
	
	<h2>Литература</h2>
	<ol>
		<li><a href="http://raphaeljs.com/reference.html#Raphael.angle">Raphael Reference - Raphael.angle</a></li>
	</ol>
</body>
</hmtl>